package carcassonne.server.http.handler;

import carcassonne.server.http.HttpContext;
import carcassonne.server.http.HttpParams;
import carcassonne.server.http.controller.UserController;
import carcassonne.server.ioc.HttpMessageDispatcher;
import carcassonne.server.http.controller.LobbyController;
import carcassonne.utils.StringUtils;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import org.apache.log4j.Logger;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HttpRequestHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static final Logger logger = Logger.getLogger(HttpRequestHandler.class);

    private final HttpMessageDispatcher messageDispatcher;

    public HttpRequestHandler() {
        List<Object> controllers = new ArrayList<>();
        controllers.add(new LobbyController());
        controllers.add(new UserController());
        messageDispatcher = new HttpMessageDispatcher(controllers);
//        messageDispatcher = new HttpMessageDispatcher("carcassonne.server.http");
    }

    @Override
    protected void channelRead0(ChannelHandlerContext context, HttpObject message) throws Exception {

        if (!(message instanceof HttpRequest request)) return;

        HttpContext httpContext = new HttpContext(request);
        String uri = request.uri();
        QueryStringDecoder queryDecoder = new QueryStringDecoder(uri, StandardCharsets.UTF_8);
        String path = queryDecoder.path();

        if (!messageDispatcher.Has(path)) {
            httpContext.Response404();
            return;
        }

        HttpMethod method = request.method();

        // URL中带的参数
        Map<String, String> params = new HashMap<>();
        for (Map.Entry<String, List<String>> attr : queryDecoder.parameters().entrySet()) {
            params.put(attr.getKey(), attr.getValue().get(0));
        }

        if (method.equals(HttpMethod.GET)) {

            messageDispatcher.HandleGet(path, httpContext, new HttpParams(request, params));

        } else if (request instanceof FullHttpRequest fullHttpRequest) {

            String contentType = GetContentType(fullHttpRequest.headers());

            switch (contentType) {
                case "multipart/form-data":
                    httpContext.ResponseForbidden("unsupported request " + contentType);
                    break;
                case "application/x-www-form-urlencoded":
                case "application/json":
                default:
                    if (method.equals(HttpMethod.POST)) {
                        messageDispatcher.HandlePost(path, httpContext, new HttpParams(fullHttpRequest, params));
                    } else if (method.equals(HttpMethod.PUT)) {
                        messageDispatcher.HandlePut(path, httpContext, new HttpParams(fullHttpRequest, params));
                    } else if (method.equals(HttpMethod.DELETE)) {
                        messageDispatcher.HandleDelete(path, httpContext, new HttpParams(fullHttpRequest, params));
                    } else {
                        httpContext.ResponseForbidden("unsupported request " + method.toString());
                    }
            }

        } else {
            httpContext.ResponseForbidden("unsupported request " + method.toString());
            return;
        }

        httpContext.Finish();
        FullHttpResponse response = httpContext.response;

        context.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
//        boolean keepAlive = HttpUtil.isKeepAlive(request);
//        if (!keepAlive) {
//            context.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
//        } else {
//            response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderNames.KEEP_ALIVE);
//            context.writeAndFlush(response);
//        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception {
        super.exceptionCaught(context, cause);
        context.writeAndFlush("an error occurred!");
        context.writeAndFlush(cause.toString());
        context.close();
    }

    public static String GetContentType(HttpHeaders headers) {
        String contentType = headers.get(HttpHeaderNames.CONTENT_TYPE);
        if (StringUtils.IsNullOrEmpty(contentType)) return "";
        String[] list = contentType.split(";");
        return list[0];
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
    }
}
